// SERVER-9406: Allow ObjectId type to be treated as a date in date related expressions

import {DateUtil} from "jstests/libs/dateutil.js";

const coll = db.server9406;
let testOpCount = 0;

coll.drop();

// Seed collection so that the pipeline will execute.
assert.commandWorked(coll.insert({}));

function makeObjectIdFromDate(dt) {
    try {
        return new ObjectId((dt.getTime() / 1000).toString(16) + "f000000000000000");
    } catch (e) {
        assert("Invalid date for conversion to Object Id: " + dt);
    }
}

/**
 *  Helper for testing that 'op' on 'value' is the same for dates as equivalent ObjectIds
 *  'value' is either a date value, or an object containing field 'date'.
 */
function testOp(op, value) {
    testOpCount++;

    let pipeline = [{$project: {_id: 0, result: {}}}];
    pipeline[0].$project.result[op] = value;
    let res1 = coll.aggregate(pipeline).toArray()[0];
    if (value.date) {
        value.date = makeObjectIdFromDate(value.date);
    } else {
        value = makeObjectIdFromDate(value);
    }
    pipeline[0].$project.result[op] = value;
    let res2 = coll.aggregate(pipeline).toArray()[0];

    assert.eq(res2.result, res1.result, tojson(pipeline));
}

testOp('$dateToString', {date: new Date("1980-12-31T23:59:59Z"), format: "%V-%G"});
testOp('$dateToString', {date: new Date("1980-12-31T23:59:59Z"), format: "%G-%V"});

const years = [
    2002,  // Starting and ending on Tuesday.
    2014,  // Starting and ending on Wednesday.
    2015,  // Starting and ending on Thursday.
    2010,  // Starting and ending on Friday.
    2011,  // Starting and ending on Saturday.
    2006,  // Starting and ending on Sunday.
    1996,  // Starting on Monday, ending on Tuesday.
    2008,  // Starting on Tuesday, ending on Wednesday.
    1992,  // Starting on Wednesday, ending on Thursday.
    2004,  // Starting on Thursday, ending on Friday.
    2016,  // Starting on Friday, ending on Saturday.
    2000,  // Starting on Saturday, ending on Sunday (special).
    2012   // Starting on Sunday, ending on Monday.
];

const day = 1;
years.forEach(function(year) {
    // forEach starts indexing at zero but weekdays start with Monday on 1 so we add +1.
    let newYear = DateUtil.getNewYear(year);
    let endOfFirstWeekInYear = DateUtil.getEndOfFirstWeekInYear(year, day);
    let startOfSecondWeekInYear = DateUtil.getStartOfSecondWeekInYear(year, day);
    let birthday = DateUtil.getBirthday(year);
    let newYearsEve = DateUtil.getNewYearsEve(year);
    let now = new Date();
    now.setYear(year);
    now.setMilliseconds(0);

    testOp('$isoDayOfWeek', newYear);
    testOp('$isoDayOfWeek', endOfFirstWeekInYear);
    testOp('$isoDayOfWeek', startOfSecondWeekInYear);
    testOp('$isoWeekYear', birthday);

    testOp('$isoWeek', newYear);
    testOp('$isoWeek', now);
    testOp('$isoWeekYear', newYear);
    testOp('$isoWeek', endOfFirstWeekInYear);
    testOp('$dateToString', {format: '%G-W%V-%u', date: newYear});
    testOp('$isoWeek', endOfFirstWeekInYear);
    testOp('$year', endOfFirstWeekInYear);
    testOp('$month', endOfFirstWeekInYear);
    testOp('$dayOfMonth', endOfFirstWeekInYear);
    testOp('$dayOfWeek', birthday);
    testOp('$dayOfWeek', newYearsEve);
    testOp('$minute', newYearsEve);
    testOp('$second', now);
    testOp('$millisecond', newYear);
});
assert.eq(testOpCount, 236, 'Expected 236 tests to run');